<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>6. 模型驱动和模板驱动的表单 | 被删的前端游乐场</title>
    <meta name="description" content="Just playing around">
    
    
    <link rel="preload" href="/front-end-playground/assets/css/0.styles.a683ed26.css" as="style"><link rel="preload" href="/front-end-playground/assets/js/app.e5cf4092.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/3.c853767c.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/2.eed49fdd.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/58.0b3b3dd2.js" as="script">
    <link rel="stylesheet" href="/front-end-playground/assets/css/0.styles.a683ed26.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/front-end-playground/" class="home-link router-link-active"><!----> <span class="site-name">被删的前端游乐场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端基础</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link router-link-active">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端基础</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link router-link-active">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-0cd75e06><span class="stand" data-v-0cd75e06></span> <div class="cat" data-v-0cd75e06><div class="body" data-v-0cd75e06></div> <div class="head" data-v-0cd75e06><div class="ear" data-v-0cd75e06></div> <div class="ear" data-v-0cd75e06></div></div> <div class="face" data-v-0cd75e06><div class="nose" data-v-0cd75e06></div> <div class="whisker-container" data-v-0cd75e06><div class="whisker" data-v-0cd75e06></div> <div class="whisker" data-v-0cd75e06></div></div> <div class="whisker-container" data-v-0cd75e06><div class="whisker" data-v-0cd75e06></div> <div class="whisker" data-v-0cd75e06></div></div></div> <div class="tail-container" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>玩转 Angular</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/front-end-playground/front-end-others/angular-free/0-about-angular-from-1-to-4.html" class="sidebar-link">谈谈 AngularJS 到 Angular</a></li><li><a href="/front-end-playground/front-end-others/angular-free/0-di-in-angular.html" class="sidebar-link">谈谈 Angular 的依赖注入</a></li><li><a href="/front-end-playground/front-end-others/angular-free/1-init-angular-app-with-webpack.html" class="sidebar-link">1. 用 Webpack 启动 Angular 应用</a></li><li><a href="/front-end-playground/front-end-others/angular-free/2-inprove-app-configuration.html" class="sidebar-link">2. 改善应用配置</a></li><li><a href="/front-end-playground/front-end-others/angular-free/3-init-routes-and-assets.html" class="sidebar-link">3. 启用路由和添加静态资源</a></li><li><a href="/front-end-playground/front-end-others/angular-free/4-create-sidebar.html" class="sidebar-link">4. 制作左侧自动定位菜单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/5-custom-input-component.html" class="sidebar-link">5. 自定义 input 表单控件</a></li><li><a href="/front-end-playground/front-end-others/angular-free/6-two-form-building.html" class="active sidebar-link">6. 模型驱动和模板驱动的表单</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/angular-free/6-two-form-building.html#angular2-表单" class="sidebar-link">Angular2 表单</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/angular-free/6-two-form-building.html#结束语" class="sidebar-link">结束语</a></li></ul></li><li><a href="/front-end-playground/front-end-others/angular-free/7-dynamic-form.html" class="sidebar-link">7. 创建动态表单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/8-radio-and-checkbox.html" class="sidebar-link">8. 表单的 radio 和 checkbox</a></li><li><a href="/front-end-playground/front-end-others/angular-free/9-upload-iamge.html" class="sidebar-link">9. 图片上传控件</a></li><li><a href="/front-end-playground/front-end-others/angular-free/10-add-conditions.html" class="sidebar-link">10. 向表单添加条件控制</a></li><li><a href="/front-end-playground/front-end-others/angular-free/11-option-dialog.html" class="sidebar-link">11. 使用动态表单制作选项配置对话框</a></li><li><a href="/front-end-playground/front-end-others/angular-free/12-config-and-create-form.html" class="sidebar-link">12. 配置以及生成表单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/13-dynamic-list.html" class="sidebar-link">13. 动态列表配置</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>试试 Cycle.js</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>D3.js 和 Tree</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>试试 Three.js</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>BOX2D.js 文档与教程</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><p>该系列用于探索一些 Angular 中灵活或者新的用法。本文简单介绍模型驱动和模板驱动的表单，以及一些表单验证。</p> <h2 id="angular2-表单"><a href="#angular2-表单" aria-hidden="true" class="header-anchor">#</a> Angular2 表单</h2> <h3 id="模板驱动"><a href="#模板驱动" aria-hidden="true" class="header-anchor">#</a> 模板驱动</h3> <p>很多时候我们都会使用模板驱动的表单，因为很方便。
使用 Angular 模板语法编写模板，便是模板驱动的表单。</p> <ol><li>FormModule</li></ol> <p>使用模板驱动的一些模板语法，我们需要引入<code>FormModule</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span> FormsModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&quot;@angular/forms&quot;</span><span class="token punctuation">;</span>
</code></pre></div><p>这样，我们的<code>&lt;form&gt;</code>和<code>&lt;input&gt;</code>等表单元素便是 Angular 元素，拥有了像<code>ngModel</code>、<code>ngSubmit</code>等指令或属性。</p> <ol start="2"><li>模板驱动的表单</li></ol> <p>这里我们使用最简单的<code>input</code>，需要输入一个 3-8 字符的名字。Component 中的 js 代码几乎没多少，而 html 模板如下：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-horizontal<span class="token punctuation">&quot;</span></span> <span class="token attr-name">#form</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>ngForm<span class="token punctuation">&quot;</span></span> <span class="token attr-name">(ngSubmit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>submit()<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-group<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>col-md-2 control-label<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>col-md-6<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span>
        <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-control<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>text<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">[(ngModel)]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>model.name<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>name<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">required</span>
        <span class="token attr-name">maxlength</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>8<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">minlength</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>3<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">#name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>ngModel<span class="token punctuation">&quot;</span></span>
      <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
    <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>alert alert-danger col-md-6 col-md-offset-2<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name">*ngIf</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>name.errors &amp;&amp; (name.dirty || name.touched)<span class="token punctuation">&quot;</span></span>
  <span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">[hidden]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>!name.errors.required<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>必填<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">[hidden]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>!name.errors.minlength<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>不小于3个字符<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">[hidden]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>!name.errors.maxlength<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>不大于8个字符<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-group<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>col-sm-offset-2 col-sm-10<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">[disabled]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>!form.valid<span class="token punctuation">&quot;</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>submit<span class="token punctuation">&quot;</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>btn btn-info<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
        提交
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><ul><li>通过<code>ngModel</code>跟踪修改状态与有效性验证</li></ul> <p>在表单中使用<code>ngModel</code>可以获得比仅使用双向数据绑定更多的控制权，不仅仅跟踪状态。它还使用特定的 Angular CSS 类来更新控件，以反映当前状态。
可以利用这些 CSS 类来修改控件的外观，显示或隐藏消息。</p> <ul><li><code>required</code>/<code>maxlength</code>/<code>minlength</code>: 用于表单校验
<ul><li><code>name.touched</code>: 控件被访问过</li> <li><code>name.dirty</code>: 控件的值变化了</li> <li><code>name.errors</code>: 控件校验错误</li></ul></li></ul> <p>指向<code>input</code>控件的引用变量上的<code>valid</code>属性，可用于检查控件是否有效、是否显示/隐藏错误信息。</p> <ul><li><code>#name=&quot;ngModel&quot;</code></li></ul> <p><code>#name</code>是模板引用变量。使用井号(<code>#</code>)来声明引用变量。
模板引用变量通常用来引用模板中的某个 DOM 元素，它还可以引用 Angular 组件或指令或 Web Component。</p> <p>指令的<code>exportAs</code>属性告诉 Angular 如何链接模板引用变量到指令。
这里把<code>name</code>设置为<code>ngModel</code>是因为<code>ngModel</code>指令的<code>exportAs</code>属性设置成了&quot;ngModel&quot;。
<code>name</code>属性的用途是有效性验证和对表单元素的变更进行追踪。</p> <ul><li>使用<code>ngSubmit</code>提交该表单</li></ul> <p>按钮位于表单的底部，它自己不做任何事，但因为有<code>type=&quot;submit&quot;</code>，所以会触发表单提交。
而要使得表单提交生效，需要把该表单的<code>ngSubmit</code>事件属性绑定到对应的提交事件。</p> <p>如果需要校验，则需要添加<code>#form=&quot;ngForm&quot;</code>，将<code>form</code>变量<code>exportAs</code>属性设置成<code>ngForm</code>，可跟踪表单有效性验证：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-horizontal<span class="token punctuation">&quot;</span></span> <span class="token attr-name">#form</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>ngForm<span class="token punctuation">&quot;</span></span> <span class="token attr-name">(ngSubmit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>submit()<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">[disabled]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>!form.valid<span class="token punctuation">&quot;</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>submit<span class="token punctuation">&quot;</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>btn btn-info<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    提交
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><h3 id="模型驱动"><a href="#模型驱动" aria-hidden="true" class="header-anchor">#</a> 模型驱动</h3> <p>模型驱动表单又称为响应式表单，它将在 html 上的一些模板语法迁移到 js 中使用。
Angular 的响应式表单能让实现响应式编程风格更容易，这种编程风格更倾向于在非 UI 的数据模型（通常接收自服务器）之间显式的管理数据流，并且用一个 UI 导向的表单模型来保存屏幕上 HTML 控件的状态和值。响应式表单可以让使用响应式编程模式、测试和校验变得更容易。</p> <p>简单地说，就是模型驱动表单更加灵活，值和状态的同步更新，以及测试性较好。</p> <ol><li>异步 vs. 同步</li></ol> <p>响应式表单是同步的。模板驱动表单是异步的。</p> <p>模板驱动表单会委托指令来创建它们的表单控件。
使用响应式表单，我们会在代码中创建整个表单控件树。 我们可以立即更新一个值或者深入到表单中的任意节点，因为所有的控件都始终是可用的。</p> <ol start="2"><li>ReactiveFormsModule</li></ol> <p>使用模型驱动的一些模板语法，我们需要引入<code>ReactiveFormsModule</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span> ReactiveFormsModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&quot;@angular/forms&quot;</span><span class="token punctuation">;</span>
</code></pre></div><ol start="3"><li>基础的表单类</li></ol> <ul><li><code>AbstractControl</code> <ul><li>是三个具体表单类的抽象基类</li> <li>为它们提供了一些共同的行为和属性，其中有些是可观察对象（<code>Observable</code>）。</li></ul></li> <li><code>FormControl</code> <ul><li>用于跟踪一个单独的表单控件的值和有效性状态</li> <li>它对应于一个 HTML 表单控件，比如输入框和下拉框</li></ul></li> <li><code>FormGroup</code> <ul><li>用于跟踪一组<code>AbstractControl</code>的实例的值和有效性状态</li> <li>该组的属性中包含了它的子控件</li> <li>组件中的顶级表单就是一个<code>FormGroup</code></li></ul></li> <li><code>FormArray</code> <ul><li>用于跟踪<code>AbstractControl</code>实例组成的有序数组的值和有效性状态</li></ul></li></ul> <ol start="4"><li><code>FormGroup</code></li></ol> <p>有多个<code>FormControl</code>，把它们注册进一个父<code>FormGroup</code>中：</p> <div class="language-js extra-class"><pre class="language-js"><code>reactiveForm <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FormGroup</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">FormControl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  email<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">FormControl</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>将<code>formGroup</code>关联到的是<code>form</code>元素上的<code>FormGroup</code>实例 reactiveForm：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span>
  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-horizontal<span class="token punctuation">&quot;</span></span>
  <span class="token attr-name">[formGroup]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>reactiveForm<span class="token punctuation">&quot;</span></span>
  <span class="token attr-name">(ngSubmit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>submit()<span class="token punctuation">&quot;</span></span>
<span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p><code>formGroup</code>是一个响应式表单的指令，它拿到一个现有<code>FormGroup</code>实例，并把它关联到一个 HTML 元素上。</p> <ol start="5"><li>formControlName</li></ol> <p>有了<code>FormGroup</code>，<code>name</code>输入框就需要再添加一个语法<code>formControlName=&quot;name&quot;</code>，以便让它关联到类中正确的<code>FormControl</code>上：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-group<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>col-md-2 control-label<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>col-md-6<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>form-control<span class="token punctuation">&quot;</span></span> <span class="token attr-name">formControlName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>name<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>这个语法告诉 Angular，查阅父<code>FormGroup</code>，然后在这个<code>FormGroup</code>中查阅一个名叫<code>name</code>的<code>FormControl</code>。</p> <ol start="6"><li>FormBuilder</li></ol> <p><code>FormBuilder</code>类能通过处理控件创建的细节问题来帮我们减少重复劳动。</p> <div class="language-js extra-class"><pre class="language-js"><code>    <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter"><span class="token keyword">private</span> fb<span class="token punctuation">:</span> FormBuilder</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 注入FormBuilder</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">createForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token function">createForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>reactiveForm <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>fb<span class="token punctuation">.</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
            name<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">''</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>
                Validators<span class="token punctuation">.</span>required<span class="token punctuation">,</span>
                Validators<span class="token punctuation">.</span><span class="token function">minLength</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                Validators<span class="token punctuation">.</span><span class="token function">maxLength</span><span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">)</span>
            <span class="token punctuation">]</span>
            <span class="token punctuation">]</span><span class="token punctuation">,</span>
            email<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">''</span><span class="token punctuation">,</span>
                <span class="token punctuation">[</span>
                    Validators<span class="token punctuation">.</span>required<span class="token punctuation">,</span>
                    Validators<span class="token punctuation">.</span>email
                <span class="token punctuation">]</span>
            <span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><p><code>FormBuilder.group</code>是一个用来创建<code>FormGroup</code>的工厂方法，它接受一个对象，对象的键和值分别是<code>FormControl</code>的名字和它的定义。
每个<code>FormControl</code>的设置都是<code>FormControl</code>名字和数组值。第一个数组元素是控件对应的当前值，第二个值（可选）是验证器函数或者验证器函数数组。</p> <p>大多数验证器函数是 Angular 以<code>Validators</code>类的静态方法的形式提供的原装验证器。</p> <ol start="7"><li>valueChanges</li></ol> <p>可以通过订阅表单控件的属性之一来了解表单控件变化。
<code>valueChanges</code>返回一个 RxJS 的 Observable 对象。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">this</span><span class="token punctuation">.</span>reactiveForm<span class="token punctuation">.</span>valueChanges<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token parameter">data</span> <span class="token operator">=&gt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onValueChanged</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>这里我们需要添加触发事件回调：</p> <div class="language-js extra-class"><pre class="language-js"><code>    <span class="token comment">// 定义表单错误</span>
    formErrors <span class="token operator">=</span> <span class="token punctuation">{</span>
        name<span class="token punctuation">:</span> <span class="token string">''</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token comment">// 对表单错误做说明映射</span>
    validationMessages <span class="token operator">=</span> <span class="token punctuation">{</span>
        name<span class="token punctuation">:</span> <span class="token punctuation">{</span>
            required<span class="token punctuation">:</span> <span class="token string">'名字必填'</span><span class="token punctuation">,</span>
            minlength<span class="token punctuation">:</span> <span class="token string">'名字最少3个字符'</span><span class="token punctuation">,</span>
            maxlength<span class="token punctuation">:</span> <span class="token string">'名字最多8个字符'</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token comment">// 表单更新触发回调</span>
    <span class="token function">onValueChanged</span><span class="token punctuation">(</span><span class="token parameter">data<span class="token operator">?</span><span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>reactiveForm<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">const</span> form <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>reactiveForm<span class="token punctuation">;</span>
        <span class="token comment">// 遍历表单控件名字</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> field <span class="token keyword">in</span> <span class="token keyword">this</span><span class="token punctuation">.</span>formErrors<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>formErrors<span class="token punctuation">[</span>field<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span> <span class="token comment">// 清除之前的错误信息</span>
            <span class="token keyword">const</span> control <span class="token operator">=</span> form<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>field<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 获取控件</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>control <span class="token operator">&amp;&amp;</span> control<span class="token punctuation">.</span>dirty <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>control<span class="token punctuation">.</span>valid<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token comment">// 遍历获取错误说明</span>
                <span class="token keyword">const</span> messages <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>validationMessages<span class="token punctuation">[</span>field<span class="token punctuation">]</span><span class="token punctuation">;</span>
                <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> key <span class="token keyword">in</span> control<span class="token punctuation">.</span>errors<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token keyword">this</span><span class="token punctuation">.</span>formErrors<span class="token punctuation">[</span>field<span class="token punctuation">]</span> <span class="token operator">+=</span> messages<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">' '</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
</code></pre></div><p>最终效果图：</p> <p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1495970774%281%29.png" alt="image"></p> <h2 id="结束语"><a href="#结束语" aria-hidden="true" class="header-anchor">#</a> 结束语</h2> <p>讲到这里，我们的两种 Angular 表单--模型驱动和模板驱动，它们的简单使用和校验已经完成。<br>
而需要更复杂一些的功能，像多级<code>FormGroup</code>、动态表单等，后面我们或许也会讲到。<br> <a href="https://github.com/godbasin/godbasin.github.io/tree/blog-codes/angular2-free/6-two-form-building" target="_blank" rel="noopener noreferrer">此处查看项目代码<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a><br> <a href="http://angular2-free.godbasin.com/6-two-form-building/index.html#/home/page-setting" target="_blank" rel="noopener noreferrer">此处查看页面效果<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/front-end-playground/edit/sourcecode/docs/front-end-others/angular-free/6-two-form-building.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div> <!----> <blockquote>部分文章中使用了一些网站的截图，如果涉及侵权，请告诉我删一下谢谢~</blockquote> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本文版权归作者所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</div> <div>出处：被删的前端游乐场</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/front-end-playground/front-end-others/angular-free/5-custom-input-component.html" class="prev">
          5. 自定义 input 表单控件
        </a></span> <span class="next"><a href="/front-end-playground/front-end-others/angular-free/7-dynamic-form.html">
          7. 创建动态表单
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-9611" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty0.svg);"></div></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/front-end-playground/assets/js/app.e5cf4092.js" defer></script><script src="/front-end-playground/assets/js/3.c853767c.js" defer></script><script src="/front-end-playground/assets/js/2.eed49fdd.js" defer></script><script src="/front-end-playground/assets/js/58.0b3b3dd2.js" defer></script>
  </body>
</html>
